﻿using System;
using System.Globalization;
using System.Linq;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.Prism.Modularity;

namespace UltimateNavigationFramework.Loaders
{
    public class UltimateViewLoadingService : IUltimateViewLoadingService
    {
        #region Private members
        private IViewCatalog _viewCatalog;
        private IUltimateModuleLoadingService _moduleLoadingService;
        #endregion

        #region Constructor
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="viewCatalog"></param>
        /// <param name="moduleLoadingService"></param>
        public UltimateViewLoadingService(IViewCatalog viewCatalog, IUltimateModuleLoadingService moduleLoadingService)
        {
            this._viewCatalog = viewCatalog;
            this._moduleLoadingService = moduleLoadingService;
        }
        #endregion

        #region Load View
        /// <summary>
        /// Loads the content
        /// </summary>
        /// <returns></returns>
        public virtual void LoadView(string uri, Action<object, bool, Exception> completeCallback, ILoadStatus loadStatus = null)
        {
            string candidateTargetContract = this.GetContractFromUri(new Uri(uri, UriKind.Relative));

            // Gets the module associated with the view
            var moduleName = this._viewCatalog.Items
                                .Where(i => i.ViewName == candidateTargetContract).Select(i => i.ModuleName)
                                .FirstOrDefault();

            // If does not exists, then throw, else load
            if (moduleName == null)
            {
                completeCallback(null, false, new InvalidOperationException(
                       string.Format("View {0} does not exist.", uri)));
                return;
            }

            if (loadStatus != null)
                loadStatus.StatusText = moduleName;

            EventHandler<UltimateModuleDownloadProgressChangedEventArgs> handler = null;

            handler = (s, e) =>
                {
                    if (e.ModuleName == moduleName)
                    {
                        if (e.ModuleDownloadStatus == UltimateModuleDownloadStatus.NotStarted ||
                            e.ModuleDownloadStatus == UltimateModuleDownloadStatus.Downloading)
                            if (loadStatus != null)
                            {
                                if (!loadStatus.IsBusy)
                                {
                                    loadStatus.IsBusy = true;
                                    loadStatus.StatusText = moduleName;
                                }

                                loadStatus.Progress = e.Percentage;
                            }
                            else if (e.ModuleDownloadStatus == UltimateModuleDownloadStatus.Downloaded ||
                            e.ModuleDownloadStatus == UltimateModuleDownloadStatus.Initialized)
                                if (loadStatus != null)
                                {
                                    loadStatus.IsBusy = false;
                                    loadStatus.Progress = e.Percentage;
                                    loadStatus.StatusText = string.Empty;
                                }
                        // Detach once completed
                        this._moduleLoadingService.ModuleDownloadStateChanged -= handler;
                    }
                };

            this._moduleLoadingService.ModuleDownloadStateChanged += handler;

            // Checks if module is already loaded, if not, then load it
            //TODO: Nulls, error handling.
            var moduleCatalog = ServiceLocator.Current.GetInstance<IModuleCatalog>();
            if (moduleCatalog.Modules.FirstOrDefault(m => m.ModuleName == moduleName) == null)
            {

            }

            this._moduleLoadingService.LoadModule(moduleName, (success, exception) =>
                {
                    if (success)
                    {
                        var view = this.CreateNewItem(candidateTargetContract);

                        completeCallback(view, success, null);
                    }
                    else
                    {
                        completeCallback(null, false, exception);
                    }

                    if (loadStatus != null && loadStatus.IsBusy)
                        loadStatus.IsBusy = false;
                    if (loadStatus != null && !String.IsNullOrEmpty(loadStatus.StatusText))
                        loadStatus.StatusText = string.Empty;
                });
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Provides a new item for the region based on the supplied candidate target contract name.
        /// </summary>
        /// <param name="candidateTargetContract">The target contract to build.</param>        
        protected virtual object CreateNewItem(string candidateTargetContract)
        {
            object newItem;
            try
            {
                newItem = ServiceLocator.Current.GetInstance<object>(candidateTargetContract);
            }
            catch (ActivationException e)
            {
                throw new InvalidOperationException(
                    string.Format(CultureInfo.CurrentCulture, "Cannot create navigation target", candidateTargetContract),
                    e);
            }
            return newItem;
        }

        /// <summary>
        /// Returns the candidate TargetContract based on the <see cref="Uri"/>.
        /// </summary>
        /// <param name="context">The uri.</param>        
        protected virtual string GetContractFromUri(Uri uri)
        {
            if (uri == null) throw new ArgumentNullException("uri");

            var candidateTargetContract = UltimateNavigationFramework.Helpers.UriParser.EnsureAbsolute(uri).AbsolutePath;
            candidateTargetContract = candidateTargetContract.TrimStart('/');
            return candidateTargetContract;
        }
        #endregion
    }
}
